using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Sandbox
{
    /// <summary>
    /// Folks: today, and today only we're gonna take us a genotype, which we reckon is gonna be a boolean array,
    /// and parse it into a set of Lisp instructions.  Free.  You heard me.  Zero smackeroos.  At no additional charge,
    /// we're also gonna take that old Stream you have in the back of your garage and put it to work.  We figure it should
    /// carry it's weight around here by letting it hold on to those shiny new commands we just generated.
    /// </summary>
    class Phenotyper
    {


        /// This class accepts a array of single-digit binary values, and converts decodes them.  Nifty.


        ///Constants
        //The maximum value of a byte to convert into a lisp command
        private const int MAX_VALUE = 14;
        //for possible use later:
        private const int MIN_VALUE = 0;
        //random number generator
        Random randGen = new Random();


        /// <summary>
        /// I need an enum to convert bits into lisp commands.  This one represents the decoding for a binary bit string.
        /// The array right after it hold the conversions.  Eventually, I'd like to take this hardcoding out so I can 
        /// simply refer to text files, but for now, this solution will do.
        /// </summary>
        /// <returns></returns>
        private enum Cmds
        {
            zero,

            one,

            two,

            three,

            four,

            five,

            six,

            seven,

            eight,

            nine,

            closePar,

            openPar,

            add,

            subtract,

            divide,

            multiply

            //There's always room for more.  When the hardcoding is taken out, we can create a series of text files
            //that represent the different things our sub-programming language (because that's what it is) can do
        }//enum Cmds

        //and the values thus taken:
        string[] cmdVals = new string[] { "0","1","","3","4","5","6","7","8","9",
                                          "(", ")", "+", "-", "/", "*", };




        /// <summary>
        /// Take an integer array that should contain only ones or zeros, and turn it into a phenotype.
        /// First, the ones and zeros should be parsed and turned into decimal, then those decimals converted into the flisp.
        /// 
        /// </summary>
        /// <returns></returns>
        public String getPhenotype(int[] genotype)
        {
            string lispLine = null; //
            
            //go through, and translate each into a String, then concatenate the String and, I dunno, return it?
            for (int x = 0; x < genotype.Length; x++)
            {
                //translate the element to a string and concatenate it into a string
                lispLine += decode(genotype[x]);

            }//for

            return lispLine;

        }//getPhenotype



        /// <summary>
        /// Decode an int into a lisp command.
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        private String decode(int x)
        {

            string lispCmd = "@______:-(_____@invalid value@______:-(_____@";

            try
            {
                lispCmd = cmdVals[x];
            }
            catch (Exception e)
            {

                //
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("ERROR ERROR ERROR ERROR ERROR " + x);
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                //the above is for debugging purposes.  I need to figure out how to make this code compile with or without
                //debugging code at will.
                Console.WriteLine(e);
            }

            return lispCmd;

        }//decode


 

        /// <summary>
        /// Takes an int[] which is essentially a byte, and converts it into decimal
        /// </summary>
        /// <param name="?"></param>
        /// An array of integers between 0 and 1 and of length 8.
        /// <returns></returns>
        private int byteToDec( int[] myByte )
        {
            int retVal = -1;

            for(int l = myByte.Length; l >= 0; l--)
            {
                retVal +=  (int)Math.Pow(2,l);
            }

            try
            {
                if (retVal < 0)
                {

                    throw new Exception();

                }
            }
            catch (Exception e)
            {

                Console.WriteLine("Hey, fool.  Something goes wrong when you try to convert a byte into a decimal.");

            }
                return retVal;

        }


        /// <summary>
        /// This will randomly mutate an individual using a double to represent the likelihood of any specific index
        /// mutating to a new valid value.  The new valid value will be a random valid value.
        /// </summary>
        /// <param name="original"></param>
        /// <param name="likelihood"></param>
        /// <returns></returns>
        public int[] mutate(int[] original, double likelihood)
        {
            int[] newOne = new int[original.Length];
            Random r = new Random();

            double l;

            for (int i = 0; i < newOne.Length; i++)
            {
                l = r.NextDouble();
                if ( l > likelihood)
                {
                    newOne[i] = r.Next(0, 15);
                }
                else
                {
                    newOne[i] = original[i];
                }
            }

            return newOne;

        }//mutate


        /// <summary>
        /// Takes two parents, finds the average length of their genotypes, creates a new, blank one of this length,
        /// does the lambada, or the forbidden dance, if you will, and crosses them over at the point equal to half the
        /// length n of the new blank one.  It then takes the first n/2 values from the shorter parent and adds it to the new
        /// one.  It fills the remainder with the longer parents ending half.  Viola.  Crosser.
        /// </summary>
        /// <param name="parentA"></param>
        /// <param name="parentB"></param>
        /// <returns></returns>
        public int[] crossover(int[] parentA, int[] parentB)
        {
            int aLength = parentA.Length;
            int bLength = parentB.Length;
     
            //reference to the shorter parent, and the longer parent
            int[] shorter;
            int[] longer;
            if(aLength<bLength)
            {
                shorter = parentA;
                longer = parentB;
            }
            else
            {
                shorter = parentB;
                longer = parentA;
            }


            //the first half of the child comes from the shorter parent
            //the second, from the longer
            int halfC_Length = (int)(aLength + bLength) / 4;

            int[] child = new int[2 * halfC_Length];

            for (int i = 0; i < aLength; i++)
            {
                child[i] = shorter[i];
            }
            for (int i = halfC_Length; i < 2 * halfC_Length; i++)
            {
                child[i] = longer[i];
            }


            return child;

        }//crossover


        //return a random genotype of length length
        public int[] getGenotype(int length)
        {
            
            //this will store the phenotype I generate
            int[] genotype = new int[length];
             
            //generate random 1s and 0s in groups of 4 which must be less than 14
            for(int i = 0; i<length; i++)
            {
                genotype[i] =  randGen.Next(MIN_VALUE,MAX_VALUE);
            }

            return genotype;

        }//getGenotype

    }//Phenotyper

}//Sandbox






///Code I don't need no more.
///
/*
       /// Convert binary into decimal by parsing bytes into decimal and return those decimals in an integer array
        /// The convention is for the number of elements in the parameter to be a multiple of 8
        private int[] bintoDec( int[] binary )
        {
            //create an int[] to store the integers
            int[] dec = new int[binary.Length];


            //a sub-array that will store me some values
            //it is the size of a byte
            int[] myByte = new int[8];

            int bIndex = 0;
            while (bIndex < binary.Length)
            {
                for (int j = 0; j < 8; j++)
                {
                    myByte[j] = binary[bIndex];
                
                    //if I've just written the last bit of the byte, convert it into a decimal integer and put it in dec[]
                    if (j == 7)
                    {
                        //dec holds 1/8 the values of Length
                        dec[binary.Length/8] = byteToDec(myByte);
                    }
                }

                //loop variable
                bIndex += 8;
                
            }


            return dec;
        }//bintoDec
*/